//
//  RBVideoPlayer.m
//  RBVideoPlayer
//
//  Created by Lan on 2017/8/4.
//  Copyright © 2017年 SummerTea. All rights reserved.
//

#import "RBMediaPlayer.h"
#import <AVFoundation/AVFoundation.h>

static NSString *const RB_STATUS_KEY = @"status";
static NSString *const RB_LOADED_TIME_RANGES_KEY = @"loadedTimeRanges";
static NSString *const RB_BUFFER_KEY = @"playbackBufferEmpty";
static NSString *const RB_MEDIA_PLAYER_SCHEME= @"Ramon";

@interface RBMediaPlayer ()<AVAssetResourceLoaderDelegate>

//播放器
@property(nonatomic, strong) AVPlayer *player;
@property(nonatomic, strong) AVPlayerLayer *playerLayer;

@property(nonatomic, assign) RBMediaPlayerStatus status;

//源文件
@property(nonatomic, strong) NSArray<NSURL *> *sources;
@property(nonatomic, assign) NSInteger currentIndex;
@property(nonatomic, strong) NSURL *currentURL;

/** 辅助处理进度条跳动问题 */
@property(nonatomic, assign) BOOL seeking;
/** 是否由系统暂停(前后台切换) */
@property(nonatomic, assign) BOOL pausedBySystem;

//忽略
@property(nonatomic, strong) id observer;

@end

@implementation RBMediaPlayer

#pragma mark - System

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        _status = NSNotFound;
        _currentIndex = NSNotFound;
    }
    return self;
}

- (void)dealloc
{
    [self removeObserver];
}

#pragma mark - Event

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:RB_STATUS_KEY])
    {
        if(self.player.status == AVPlayerStatusReadyToPlay)
        {
            self.status = RBMediaPlayerStatusReadyToPlay;
        }
        else
        {
            self.status = RBMediaPlayerStatusUnavailable;
        }
    }
    else if([keyPath isEqualToString:RB_LOADED_TIME_RANGES_KEY])
    {
        CMTimeRange timeRange = [self.player.currentItem.loadedTimeRanges.firstObject CMTimeRangeValue];
        NSTimeInterval totalBuffer = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration);
        CGFloat duration = CMTimeGetSeconds(self.player.currentItem.duration);
        duration = isnan(duration)? 0: duration;
        totalBuffer = isnan(totalBuffer)? 0: totalBuffer;
        if (self.cacheBlock)
        {
            self.cacheBlock(duration, totalBuffer, self.sources[self.currentIndex]);
        }
    }
    else if([keyPath isEqualToString:RB_BUFFER_KEY])
    {
        self.status = RBMediaPlayerStatusBuffering;
    }
}

- (void)enterBackground
{
    if (self.playing)
    {
        [self pause];
        self.pausedBySystem = YES;
    }
    else
    {
        self.pausedBySystem = NO;
    }
}

- (void)enterForeground
{
    if (self.pausedBySystem)
    {
        [self resume];
    }
}

- (void)playToEnd
{
    self.status = RBMediaPlayerStatusPlayToEnd;
}

- (void)failPlayToEnd
{
//    NSLog(@"==%s", __FUNCTION__);
}

- (void)playError
{
//    NSLog(@"==%s",  __FUNCTION__);
}

- (void)accessLogEntry: (NSNotification *)noti;
{
    id obj = noti.object;
    NSDictionary *dic = noti.userInfo;
//    NSLog(@"==%s", __FUNCTION__);
}

- (void)playbackInstalled
{
//    NSLog(@"==%s", __FUNCTION__);
}

#pragma mark - Private

- (void)addObserver
{
    if (_player.currentItem)
    {
        __weak typeof(self) weak_self = self;
        [self.player.currentItem addObserver:self forKeyPath:RB_STATUS_KEY options:NSKeyValueObservingOptionNew context:nil];
        [self.player.currentItem addObserver:self forKeyPath:RB_LOADED_TIME_RANGES_KEY options:NSKeyValueObservingOptionNew context:nil];
        [self.player.currentItem addObserver:self forKeyPath:RB_BUFFER_KEY options:NSKeyValueObservingOptionNew context:nil];
        self.observer = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10) queue:dispatch_get_main_queue() usingBlock:^(CMTime time)
        {
            CGFloat duration = CMTimeGetSeconds(weak_self.player.currentItem.duration);
            CGFloat currentTime = CMTimeGetSeconds(time);
            if (!weak_self.seeking)
            {
                if (weak_self.player.rate > 0)
                {
                    
//                    NSLog(@"rate:%.1f", weak_self.player.rate);
                    weak_self.status = RBMediaPlayerStatusPlaying;
                }
                else
                {
                    //如果已经暂停了,屏蔽加载状态RBMediaPlayerStatusBuffering
                    if (weak_self.status != RBMediaPlayerStatusPaused)
                    {
                        weak_self.status = RBMediaPlayerStatusBuffering;
                    }
                }
                if (weak_self.durationBlock)
                {
                    weak_self.durationBlock(duration, currentTime, weak_self.sources[weak_self.currentIndex]);
                }
            }
        }];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterBackground) name:UIApplicationWillResignActiveNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterForeground) name:UIApplicationDidBecomeActiveNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playToEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(failPlayToEnd) name:AVPlayerItemFailedToPlayToEndTimeNotification object:self.player.currentItem];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playError) name:AVPlayerItemNewErrorLogEntryNotification object:self.player.currentItem];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(accessLogEntry:) name:AVPlayerItemNewAccessLogEntryNotification object:self.player.currentItem];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackInstalled) name:AVPlayerItemPlaybackStalledNotification object:self.player.currentItem];
    }
}


- (void)addd: (NSNotification *)noti
{
    id obj = noti.object;
    NSDictionary *info = noti.userInfo;
//    NSLog(@"%@", noti.name);
}

- (void)removeObserver
{
    if (_player.currentItem)
    {
        [self.player.currentItem removeObserver:self forKeyPath:RB_STATUS_KEY];
        [self.player.currentItem removeObserver:self forKeyPath:RB_LOADED_TIME_RANGES_KEY];
        [self.player.currentItem removeObserver:self forKeyPath:RB_BUFFER_KEY];
        [self.player removeTimeObserver:self.observer];
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
}

#pragma mark - Public

+ (instancetype)player
{
    static id instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [self new];
    });
    return instance;
}

- (void)setSources:(NSMutableArray<NSURL *> *)sources
{
    _sources = sources;
}

- (BOOL)getReadyToPlay:(NSInteger)index
{
    
    if (!self.sources.count)
    {
        self.currentIndex = NSNotFound;
        self.currentURL = nil;
        [self stop];
        return NO;
    }
    else
    {
        self.currentIndex = index < self.sources.count? index: 0;
        if (![self.currentURL.absoluteString isEqualToString:((NSURL *)self.sources[self.currentIndex]).absoluteString])
        {
            [self removeObserver];
            self.currentURL = self.sources[self.currentIndex];
            NSURL *currentURL = self.currentURL;
            NSURLComponents *components = [[NSURLComponents alloc]initWithURL:currentURL resolvingAgainstBaseURL:NO];
            //注意，不加这一句不能执行到回调操作
//            components.scheme = RB_MEDIA_PLAYER_SCHEME;
            AVURLAsset *asset = [AVURLAsset URLAssetWithURL:components.URL options:nil];
            [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
            AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
            item.canUseNetworkResourcesForLiveStreamingWhilePaused = YES;
            [self.player replaceCurrentItemWithPlayerItem:item];
            [self addObserver];
        }
        return YES;
    }
}

- (void)play
{
    [self playWithIndex:0];
}

- (void)playWithIndex:(NSInteger)index
{
    BOOL ready = [self getReadyToPlay:index];
    if (ready)
    {
        [self.player play];
        self.status = RBMediaPlayerStatusBuffering;
    }
    else
    {
        self.status = RBMediaPlayerStatusUnavailable;
    }
}

- (void)pause
{
    [_player pause];
    self.status = RBMediaPlayerStatusPaused;
}

- (void)resume
{
    [self.player play];
    self.status = RBMediaPlayerStatusBuffering;
}

- (void)stop
{
    [self removeObserver];
    [self pause];
    _player = nil;
    [self removePicture];
    _playerLayer = nil;
    self.status = RBMediaPlayerStatusUnavailable;
}

- (void)seekToTime:(CGFloat)time
{
//    if (self.player.status = AVPlayerStatusReadyToPlay)
//    {
//        
//    }
    self.seeking = YES;
    BOOL playing = self.playing;
    if (playing)
    {
        [self.player pause];
        self.status = RBMediaPlayerStatusBuffering;
    }
    __weak typeof(self) weak_self = self;
    [self.player.currentItem seekToTime:CMTimeMake(time, 1.0) completionHandler:^(BOOL finished) {
        self.seeking = NO;
        if (playing)
        {
            [weak_self.player play];
            weak_self.status = RBMediaPlayerStatusBuffering;
        }
    }];
}

- (void)addPictureToLayer:(CALayer *)layer frame:(CGRect)frame
{
    if (self.playerLayer)
    {
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        self.playerLayer.frame = frame;
        [CATransaction commit];
        [layer addSublayer:self.playerLayer];
    }
}

- (void)setPictureFrame:(CGRect)frame
{
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.playerLayer.frame = frame;
    [CATransaction commit];
}

- (void)removePicture
{
    [self.playerLayer removeFromSuperlayer];
}

- (CGFloat)duration
{
    return CMTimeGetSeconds(self.player.currentItem.duration);
}

- (CGFloat)currentTime
{
    return CMTimeGetSeconds(self.player.currentItem.currentTime);
}

#pragma mark - AVAssetResourceLoaderDelegate

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    //获取系统中不能处理的URL
    NSURL *resourceURL = [loadingRequest.request URL];
    //判断这个URL是否遵守URL规范和其是否是我们所设定的URL
    if ([resourceURL.scheme isEqualToString:RB_MEDIA_PLAYER_SCHEME]){
        //判断当前的URL网络请求是否已经被加载过了，如果缓存中里面有URL对应的网络加载器(自己封装，也可以直接使用NSURLRequest)，则取出来添加请求，每一个URL对应一个网络加载器，loader的实现接下来会说明
        /*
        AVResourceLoaderForASI *loader = [self asiresourceLoaderForRequest:loadingRequest];
        if (loader == nil){
            loader = [[AVResourceLoaderForASI alloc] initWithResourceURL:resourceURL];
            loader.delegate = self;
            4//缓存网络加载器
            [self.resourceLoaders setObject:loader forKey:[self keyForResourceLoaderWithURL:resourceURL]];
        }
        //加载器添加请求
        [loader addRequest:loadingRequest];
        //返回YES则表明使用我们的代码对AVAsset中请求网络资源做处理
         */
        return YES;
    }else{
        return NO;
    }
}

- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
{
    //如果用户在下载的过程中调用者取消了获取视频,则从缓存中取消这个请求
//    NSURL *resourceURL = [loadingRequest.request URL];
//    NSString *actualURLString = [self actualURLStringWithURL:resourceURL];
//    AVResourceLoaderForASI *loader = [_resourceLoaders objectForKey:actualURLString];
//    [loader removeRequest:loadingRequest];
}

#pragma mark - Setter

- (void)setStatus:(RBMediaPlayerStatus)status
{
    if (_status == status)
    {
        return;
    }
    
    //暂停进入后台,再返回前台,可能会触发RBMediaPlayerStatusReadyToPlay,屏蔽一下
    if (_status == RBMediaPlayerStatusPaused && status == RBMediaPlayerStatusReadyToPlay)
    {
        return;
    }
    
    NSString *text = @"dd";
    switch (status)
    {
        case RBMediaPlayerStatusReadyToPlay:
            text = @"准备播放";
            break;
        case RBMediaPlayerStatusUnavailable:
            text = @"不可用";
            break;
        case RBMediaPlayerStatusPlaying:
            text = @"播放中";
            break;
        case RBMediaPlayerStatusBuffering:
            text = @"缓冲";
            break;
        case RBMediaPlayerStatusPaused:
            text = @"暂停";
            break;
        case RBMediaPlayerStatusPlayToEnd:
            text = @"播放完了";
            break;
        default:
            break;
    }
//    NSLog(@"status:%@", text);
    _status = status;
    if (self.statusBlock)
    {
        self.statusBlock(status, self.currentURL);
    }
}

- (void)setRate:(CGFloat)rate
{
    self.player.rate = rate;
}

#pragma mark - Getter

- (BOOL)playing
{
    return self.status == RBMediaPlayerStatusPlaying || self.status == RBMediaPlayerStatusBuffering || self.status == RBMediaPlayerStatusReadyToPlay;
}

- (CGFloat)rate
{
    return self.player.rate;
}

- (AVPlayer *)player
{
    if (!_player)
    {
        self.player = ({
            AVPlayer *player = [AVPlayer playerWithPlayerItem:nil];
            if (@available(iOS 10.0, *)) {
                [player setAutomaticallyWaitsToMinimizeStalling:NO];
            }
            player;
        });
    }
    return _player;
}

- (AVPlayerLayer *)playerLayer
{
    if (!_playerLayer)
    {
        self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    }
    return _playerLayer;
}

@end
